Micron Document
πŸŽ–οΈGitΠ―Ρ€Π°πŸŽ–οΈ

Node / meshtastic / Meshtastic-Android / files / docs / decisions / navigation3-parity-2026-03.md

Displaying Rendered β€’ View raw β€’ Download

docs/decisions/navigation3-parity-2026-03.md 20d934459a489ffab53d9dfd88e841ea3df58e6a (20d93445) Text, 10.59 KB

<!--
β€’ Copyright (c) 2026 Meshtastic LLC
-
β€’ This program is free software: you can redistribute it and/or modify
β€’ it under the terms of the GNU General Public License as published by
β€’ the Free Software Foundation, either version 3 of the License, or
β€’ (at your option) any later version.
-->

Navigation 3 Parity Strategy (Android + Desktop)

Date: 2026-03-11
Status: Implemented (2026-03-21)
Scope: T383838app and T383838desktop navigation structure using shared T383838core:navigation routes

Context

Desktop and Android both use Navigation 3 typed routes from T383838core:navigation. Previously graph wiring had diverged β€” desktop used a separate T383838DesktopDestination enum with 6 entries (including a top-level Firmware tab) while Android used 5 entries.

This has been resolved. Both shells now use the shared T383838TopLevelDestination enum from T383838core:navigation/commonMain with 5 entries (Conversations, Nodes, Map, Settings, Connections). Firmware is an in-flow route on both platforms.

Both modules still define separate graph-builder files (T383838app/navigation/*.kt, T383838desktop/navigation/*.kt) with different destination coverage and placeholder behavior, but the top-level shell structure is unified.

Current-State Findings

1. Top-level destinations are unified.
β€’ Both shells iterate T383838TopLevelDestination.entries from T383838core:navigation/commonMain.
β€’ Shared icon mapping lives in T383838core:ui (T383838TopLevelDestinationExt.icon).
β€’ Parity tests exist in both T383838core:navigation/commonTest (T383838NavigationParityTest) and T383838desktop/test (T383838DesktopTopLevelDestinationParityTest).
2. Feature coverage is unified via T383838commonMain feature graphs.
β€’ The T383838settingsGraph, T383838nodesGraph, T383838contactsGraph, T383838connectionsGraph, T383838firmwareGraph, and T383838mapGraph are now fully shared and exported from their respective feature modules' T383838commonMain source sets.
β€’ Desktop acts as a thin shell, delegating directly to these shared graphs.
3. Saved-state route registration is fully shared.
β€’ T383838MeshtasticNavSavedStateConfig in T383838core:navigation/commonMain maintains the unified T383838SavedStateConfiguration serializer list.
β€’ Both Android and Desktop reference this shared config when instantiating T383838rememberNavBackStack.
4. Predictive back handling is KMP native.
β€’ Custom T383838PredictiveBackHandler wrapper was removed in favor of Jetpack's official KMP T383838NavigationBackHandler from T383838androidx.navigationevent:navigationevent-compose.

Alpha04 β†’ Beta01 Changelog Impact Check

Source reviewed: Navigation 3 T3838381.1.0-beta01 (JetBrains fork), CMP T3838381.11.0-beta01, Lifecycle T3838382.11.0-alpha02.

β”‚ Superseded by:
β”‚ for the full API surface audit and Scene architecture adoption plan.

1. NavDisplay API updated to Scene-based architecture.
β€’ The T383838sceneStrategy: SceneStrategy<T> parameter is deprecated in favor of T383838sceneStrategies: List<SceneStrategy<T>>.
β€’ New T383838sceneDecoratorStrategies: List<SceneDecoratorStrategy<T>> parameter available.
β€’ New T383838sharedTransitionScope: SharedTransitionScope? parameter for shared element transitions.
β€’ Existing shell patterns in T383838app and T383838desktop remain valid using the default T383838SinglePaneSceneStrategy.
2. Entry-scoped ViewModel lifecycle adopted.
β€’ Both T383838app and T383838desktop now use T383838MeshtasticNavDisplay (T383838core:ui/commonMain), which applies T383838ViewModelStoreNavEntryDecorator + T383838SaveableStateHolderNavEntryDecorator per active backstack.
β€’ ViewModels obtained via T383838koinViewModel() inside T383838entry<T> blocks are now scoped to the entry's backstack lifetime.
3. No direct Navigation 3 API breakage.
β€’ Release is beta (API stabilized). No migration from alpha04 was required for existing usage patterns.
4. Primary risk is dependency wiring drift, not runtime behavior.
β€’ JetBrains Navigation 3 currently publishes T383838navigation3-ui coordinates (no separate T383838navigation3-runtime artifact in Maven Central). The T383838jetbrains-navigation3-runtime alias intentionally points to T383838navigation3-ui and is documented in the version catalog.
β€’ Note: The T383838remember* composable factory functions from T383838navigation3-runtime are not visible in non-KMP Android modules due to Kotlin metadata resolution. Use direct class constructors instead (as done in T383838app/Main.kt).
5. Saved-state and typed-route parity improved.
β€’ Both hosts share T383838MeshtasticNavSavedStateConfig from T383838core:navigation/commonMain via T383838MultiBackstack, reducing platform drift risk in serializer registration.
6. Updated active docs to reflect the current dependency baseline (T3838381.11.0-beta01, T3838381.1.0-beta01, T3838381.3.0-alpha06, T3838382.11.0-alpha02).

Actions Taken

β€’ Renamed all JetBrains-forked lifecycle/nav3 version catalog aliases from T383838androidx-* to T383838jetbrains-* prefix to make fork provenance unambiguous:
β€’ T383838jetbrains-lifecycle-runtime, T383838jetbrains-lifecycle-runtime-compose, T383838jetbrains-lifecycle-viewmodel-compose, T383838jetbrains-lifecycle-viewmodel-navigation3
β€’ T383838jetbrains-navigation3-runtime, T383838jetbrains-navigation3-ui
β€’ Documented in the version catalog that T383838jetbrains-navigation3-runtime intentionally maps to T383838navigation3-ui until a separate runtime artifact is published.
β€’ Migrated T383838core:data T383838commonMain from T383838androidx.lifecycle:lifecycle-runtime (Google) to T383838org.jetbrains.androidx.lifecycle:lifecycle-runtime (JetBrains fork) for full consistency.
β€’ Updated active docs to reflect the current dependency baseline (T3838381.11.0-beta01, T3838381.1.0-beta01, T3838381.3.0-alpha06, T3838382.11.0-alpha02).
β€’ Consolidated T383838app adaptive dependencies to JetBrains Material 3 Adaptive coordinates (T383838org.jetbrains.compose.material3.adaptive:*) so Android and Desktop consume the same adaptive artifact family. The Android-only navigation suite remains on T383838androidx.compose.material3:material3-adaptive-navigation-suite.

Deferred Follow-ups

β€’ Add automated validation that desktop serializer registrations stay in sync with shared route keys.

Options Evaluated

Option A: Reuse T383838:app navigation implementation directly in desktop

Pros
β€’ Maximum short-term parity in structure.

Cons
β€’ T383838:app graph code is tightly coupled to Android wrappers (T383838Android*ViewModel, Android-only screen wrappers, app-specific UI state like scroll-to-top flows).
β€’ Pulling this code into desktop would either fail at compile-time or force additional platform branching in app files.
β€’ Violates clean module boundaries (T383838desktop should not depend on Android-specific app glue).

Decision: Not recommended.

Option B: Keep fully separate desktop graph and replicate app behavior manually

Pros
β€’ Lowest refactor cost right now.
β€’ Keeps platform customization simple.

Cons
β€’ Drift is guaranteed over time.
β€’ No central policy for intentional vs accidental divergence.
β€’ High maintenance burden for parity-sensitive flows.

Decision: Not recommended as a long-term strategy.

Option C (Recommended): Hybrid shared contract + platform graph adapters

Pros
β€’ Preserves platform-specific wiring where needed.
β€’ Reduces drift by moving parity-sensitive definitions to shared contracts.
β€’ Enables explicit, testable exceptions for desktop-only or Android-only behavior.

Cons
β€’ Requires incremental extraction work.
β€’ Needs light governance (parity matrix + tests + docs).

Decision: Recommended.

Decision

Adopt a hybrid parity model:

1. Keep platform graph registration in T383838app and T383838desktop.
2. Extract parity-sensitive navigation metadata into shared contracts (top-level destination set/order, route ownership map, and allowed platform exceptions).
3. Keep platform-specific destination implementations as adapters around shared route keys.
4. Add route parity tests so drift is detected automatically.

Implementation Plan

Phase 1 (Immediate): Stop drift on shell structure βœ…

β€’ βœ… Aligned desktop top-level destination policy with Android (removed Firmware from top-level; kept as in-flow).
β€’ βœ… Both shells now use shared T383838TopLevelDestination enum from T383838core:navigation/commonMain.
β€’ βœ… Shared icon mapping in T383838core:ui (T383838TopLevelDestinationExt.icon).
β€’ Parity matrix documented inline: top-level set is Conversations, Nodes, Map, Settings, Connections on both platforms.

Phase 2 (Near-term): Extract shared navigation contracts βœ… (partially)

β€’ βœ… Shared T383838TopLevelDestination enum with T383838fromNavKey() already serves as the canonical metadata object.
β€’ Both T383838app and T383838desktop shells iterate T383838TopLevelDestination.entries β€” no separate T383838DesktopDestination enum remains.
β€’ Remaining: optional visibility flags by platform, route grouping metadata (lower priority since shells are unified).

Phase 3 (Near-term): Add parity checks βœ… (partially)

β€’ βœ… T383838NavigationParityTest in T383838core:navigation/commonTest β€” asserts 5 top-level destinations and T383838fromNavKey matching.
β€’ βœ… T383838DesktopTopLevelDestinationParityTest in T383838desktop/test β€” asserts desktop routes match Android parity set and firmware is not top-level.
β€’ Remaining: assert every desktop serializer registration corresponds to an actual route; assert every intentional exception is listed.

Phase 4 (Mid-term): Reduce app-specific graph coupling

β€’ Move reusable graph composition helpers out of T383838:app where practical (while keeping Android-only wrappers in Android source sets).
β€’ Keep desktop-specific placeholder implementations, but tie them to explicit parity exception entries.

Consequences

β€’ Navigation behavior remains platform-adaptive, but parity expectations become explicit and enforceable.
β€’ Desktop can keep legitimate deviations (map/charts/platform integrations) without silently changing IA.
β€’ New route additions will require touching one shared contract plus platform implementations, making review scope clearer.

Source Anchors

β€’ Shared routes: T383838core/navigation/src/commonMain/kotlin/org/meshtastic/core/navigation/Routes.kt
β€’ Shared saved-state config: T383838core/navigation/src/commonMain/kotlin/org/meshtastic/core/navigation/NavigationConfig.kt
β€’ Android shell: T383838app/src/main/kotlin/org/meshtastic/app/ui/Main.kt
β€’ Shared graph registrations: T383838feature/*/src/commonMain/kotlin/org/meshtastic/feature/*/navigation/
β€’ Platform graph content: T383838feature/*/src/{androidMain,jvmMain}/kotlin/org/meshtastic/feature/*/navigation/
β€’ Desktop shell: T383838desktop/src/main/kotlin/org/meshtastic/desktop/ui/DesktopMainScreen.kt
β€’ Desktop graph assembly: T383838desktop/src/main/kotlin/org/meshtastic/desktop/navigation/DesktopNavigation.kt

Served by rngit 1.5.2 - Generated in 0.03s